package org.rslai.autotest.core.page.impl;

import java.util.List;

import org.openqa.selenium.By;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.rslai.autotest.core.page.AbstractHelper;
import org.rslai.autotest.core.page.Conf;
import org.rslai.autotest.core.page.Wait;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * Wait处理方法
 * @author rslai
 */
public class WaitImpl extends AbstractHelper implements Wait {

	@Autowired
	private Conf conf;

	@Override
	public WebElement waitForElement(final By by) {
		WebDriverWait wait = new WebDriverWait(this.driver(), this.conf.getWebdriverwaitTimeout());
		WebElement webElement = wait.until(new ExpectedCondition<WebElement>() {
			public WebElement apply(WebDriver d) {
				return d.findElement(by);
			}
		});
		
		return webElement;
	}

	@Override
	public WebElement waitForElement(final WebElement element, final By by) {
		WebDriverWait wait = new WebDriverWait(this.driver(), this.conf.getWebdriverwaitTimeout());
		WebElement webElement = wait.until(new ExpectedCondition<WebElement>() {
			public WebElement apply(WebDriver driver) {

				// XPath 表达式容错处理
				if (by instanceof By.ByXPath) {
					if (by.toString().indexOf(" //") != -1) {
						IllegalArgumentException e = new IllegalArgumentException("在某个元素上使用 XPath 表达式查找子元素时，XPath 表达式中使用“//”的同时必须使用“child::node()”或“.//”，否则将会在整个 dom 中查找元素，而不是在你指定的元素下查找。\r\n\t"
								+ "例1：waitForElement(we, By.xpath(\"../child::node()//input\")); 此语法将在 we 元素下查找 input 标签的元素。\r\n\t"
								+ "例2：waitForElement(we, By.xpath(\".//input\")); 此语法将在 we 元素下查找 input 标签的元素。");
						e.printStackTrace();
						throw e;
					}
				}

				return element.findElement(by);
			}

		});
		return webElement;
	}

	@Override
	public List<WebElement> waitForElements(final By by) {
		return super.driver().findElements(by);
	}

	@Override
	public List<WebElement> waitForElements(final WebElement element, final By by) {
		// XPath 表达式容错处理
		if (by instanceof By.ByXPath) {
			if (by.toString().indexOf(" //") != -1) {
				IllegalArgumentException e = new IllegalArgumentException("在某个元素上使用 XPath 表达式查找子元素时，XPath 表达式中使用“//”的同时必须使用“child::node()”或“.//”，否则将会在整个 dom 中查找元素，而不是在你指定的元素下查找。\r\n\t"
						+ "例1：waitForElement(we, By.xpath(\"../child::node()//input\")); 此语法将在 we 元素下查找 input 标签的元素。\r\n\t"
						+ "例2：waitForElement(we, By.xpath(\".//input\")); 此语法将在 we 元素下查找 input 标签的元素。");
				e.printStackTrace();
				throw e;
			}
		}
		return element.findElements(by);
	}

	@Override
	public boolean waitForHiddenElement(By baseBy, String xpathExpression) {
		return this.waitForHiddenElement(baseBy, xpathExpression, this.conf.getWebdriverwaitTimeout());
	}

	@Override
	public boolean waitForHiddenElement(By baseBy, String xpathExpression, int waitTimeout) {
		for (int i=0; i<waitTimeout; i++) {
			if (!this.isDisplayedElement(baseBy, xpathExpression)) {
				return true;
			}
			this.sleep(1);
		}

		throw new TimeoutException("超时错误：经过" + waitTimeout + "秒后元素 (By.xpath: " + xpathExpression + ")仍然可见。");
	}

	@Override
	public boolean waitForHiddenElement(By baseBy, WebElement element, String xpathExpression) {
		return this.waitForHiddenElement(baseBy, element, xpathExpression, this.conf.getWebdriverwaitTimeout());
	}

	@Override
	public boolean waitForHiddenElement(By baseBy, WebElement element, String xpathExpression, int waitTimeout) {
		// XPath 表达式容错处理
		if (xpathExpression.indexOf("//") == 0) {
			IllegalArgumentException e = new IllegalArgumentException("等待一个元素下的某个WebElement消失或隐藏。参数 xpathExpression 中使用“//”的同时必须使用“child::node()”或“.//”，否则将会在整个 dom 中查找元素，而不是在你指定的元素下查找。\r\n\t"
					+ "例1：xpathExpression = \"../child::node()//input\" 此语法将在元素下查找 input 标签的元素。\r\n\t"
					+ "例2：xpathExpression = \".//input\" 此语法将在元素下查找 input 标签的元素。");
			e.printStackTrace();
			throw e;
		}

		for (int i=0; i<waitTimeout; i++) {
			if (!this.isDisplayedElement(baseBy, element, xpathExpression)) {
				return true;
			}
			this.sleep(1);
		}

		throw new TimeoutException("超时错误：经过" + waitTimeout + "秒后元素 (By.xpath: " + xpathExpression + ")仍然可见。");
	}

	@Override
	public Boolean isDisplayedElement(By baseBy, String xpathExpression) {
		this.waitForElement(baseBy);

		List<WebElement> elements = this.driver().findElements(By.xpath("//html | " + xpathExpression));
		int len = elements.size();
		if (len == 1) {
			return false;
		} else {
			return elements.get(1).isDisplayed();
		}
	}

	@Override
	public Boolean isDisplayedElement(By baseBy, WebElement element, String xpathExpression) {
		// XPath 表达式容错处理
		if (xpathExpression.indexOf("//") == 0) {
			IllegalArgumentException e = new IllegalArgumentException("返回一个元素下的某个元素是否可见。参数 xpathExpression 中使用“//”的同时必须使用“child::node()”或“.//”，否则将会在整个 dom 中查找元素，而不是在你指定的元素下查找。\r\n\t"
					+ "例1：xpathExpression = \"../child::node()//input\" 此语法将在元素下查找 input 标签的元素。\r\n\t"
					+ "例2：xpathExpression = \".//input\" 此语法将在元素下查找 input 标签的元素。");
			e.printStackTrace();
			throw e;
		}

		this.waitForElement(baseBy);

		List<WebElement> elements = element.findElements(By.xpath("//html | " + xpathExpression));
		int len = elements.size();
		if (len == 1) {
			return false;
		} else {
			return elements.get(1).isDisplayed();
		}
	}

}
